package zgrjb.snbq.teach_platform.component;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 通知WebSocket处理器
 * 处理实时通知推送
 * 
 * @author system
 * @since 2024-12-28
 */
@Component
@Slf4j
public class NotificationWebSocketHandler implements WebSocketHandler {

    // 存储用户会话，key为用户ID，value为WebSocket会话
    private static final Map<Long, WebSocketSession> userSessions = new ConcurrentHashMap<>();
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        log.info("通知WebSocket连接建立: {}", session.getId());
        
        // 从session中获取用户ID
        Long userId = getUserIdFromSession(session);
        if (userId != null) {
            userSessions.put(userId, session);
            log.info("用户 {} 的通知WebSocket连接已建立", userId);
            
            // 发送连接成功通知
            sendWelcomeMessage(session, userId);
        }
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        if (message instanceof TextMessage) {
            handleTextMessage(session, (TextMessage) message);
        }
    }

    private void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        log.info("收到通知WebSocket消息: {}", message.getPayload());
        
        try {
            Map<String, Object> msgData = objectMapper.readValue(message.getPayload(), Map.class);
            String type = (String) msgData.get("type");
            
            switch (type) {
                case "ping":
                    handlePing(session);
                    break;
                case "subscribe":
                    handleSubscribe(session, msgData);
                    break;
                case "unsubscribe":
                    handleUnsubscribe(session, msgData);
                    break;
                case "mark_read":
                    handleMarkRead(session, msgData);
                    break;
                default:
                    log.warn("未知通知类型: {}", type);
            }
        } catch (Exception e) {
            log.error("处理通知WebSocket消息失败", e);
        }
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.error("通知WebSocket传输错误: {}", session.getId(), exception);
        removeSession(session);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        log.info("通知WebSocket连接关闭: {}, 状态: {}", session.getId(), closeStatus);
        removeSession(session);
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    /**
     * 发送欢迎消息
     */
    private void sendWelcomeMessage(WebSocketSession session, Long userId) throws IOException {
        String welcomeMessage = objectMapper.writeValueAsString(Map.of(
            "type", "welcome",
            "data", Map.of(
                "message", "通知WebSocket连接成功",
                "userId", userId
            ),
            "timestamp", System.currentTimeMillis()
        ));
        session.sendMessage(new TextMessage(welcomeMessage));
    }

    /**
     * 处理ping消息
     */
    private void handlePing(WebSocketSession session) throws IOException {
        String pongMessage = objectMapper.writeValueAsString(Map.of(
            "type", "pong",
            "timestamp", System.currentTimeMillis()
        ));
        session.sendMessage(new TextMessage(pongMessage));
    }

    /**
     * 处理订阅通知
     */
    private void handleSubscribe(WebSocketSession session, Map<String, Object> msgData) throws IOException {
        Map<String, Object> data = (Map<String, Object>) msgData.get("data");
        String notificationType = (String) data.get("notificationType");
        
        Long userId = getUserIdFromSession(session);
        log.info("用户 {} 订阅通知类型: {}", userId, notificationType);
        
        String response = objectMapper.writeValueAsString(Map.of(
            "type", "subscribed",
            "data", Map.of(
                "notificationType", notificationType,
                "status", "success"
            ),
            "timestamp", System.currentTimeMillis()
        ));
        session.sendMessage(new TextMessage(response));
    }

    /**
     * 处理取消订阅
     */
    private void handleUnsubscribe(WebSocketSession session, Map<String, Object> msgData) throws IOException {
        Map<String, Object> data = (Map<String, Object>) msgData.get("data");
        String notificationType = (String) data.get("notificationType");
        
        Long userId = getUserIdFromSession(session);
        log.info("用户 {} 取消订阅通知类型: {}", userId, notificationType);
        
        String response = objectMapper.writeValueAsString(Map.of(
            "type", "unsubscribed",
            "data", Map.of(
                "notificationType", notificationType,
                "status", "success"
            ),
            "timestamp", System.currentTimeMillis()
        ));
        session.sendMessage(new TextMessage(response));
    }

    /**
     * 处理标记已读
     */
    private void handleMarkRead(WebSocketSession session, Map<String, Object> msgData) throws IOException {
        Map<String, Object> data = (Map<String, Object>) msgData.get("data");
        Long notificationId = Long.valueOf(data.get("notificationId").toString());
        
        Long userId = getUserIdFromSession(session);
        log.info("用户 {} 标记通知 {} 为已读", userId, notificationId);
        
        String response = objectMapper.writeValueAsString(Map.of(
            "type", "marked_read",
            "data", Map.of(
                "notificationId", notificationId,
                "status", "success"
            ),
            "timestamp", System.currentTimeMillis()
        ));
        session.sendMessage(new TextMessage(response));
    }

    /**
     * 发送通知给指定用户
     */
    public static void sendNotificationToUser(Long userId, Object notification) {
        WebSocketSession session = userSessions.get(userId);
        if (session != null && session.isOpen()) {
            try {
                ObjectMapper mapper = new ObjectMapper();
                Map<String, Object> message = Map.of(
                    "type", "notification",
                    "data", notification,
                    "timestamp", System.currentTimeMillis()
                );
                String jsonMessage = mapper.writeValueAsString(message);
                session.sendMessage(new TextMessage(jsonMessage));
                log.info("发送通知给用户 {}: {}", userId, notification);
            } catch (Exception e) {
                log.error("发送WebSocket通知给用户 {} 失败", userId, e);
            }
        } else {
            log.debug("用户 {} 的WebSocket连接不存在或已关闭", userId);
        }
    }

    /**
     * 发送系统通知给所有在线用户
     */
    public static void broadcastSystemNotification(Object notification) {
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> message = Map.of(
            "type", "system_notification",
            "data", notification,
            "timestamp", System.currentTimeMillis()
        );
        
        userSessions.values().forEach(session -> {
            if (session.isOpen()) {
                try {
                    String jsonMessage = mapper.writeValueAsString(message);
                    session.sendMessage(new TextMessage(jsonMessage));
                } catch (Exception e) {
                    log.error("广播系统通知失败", e);
                }
            }
        });
        log.info("广播系统通知给所有在线用户: {}", notification);
    }

    /**
     * 发送紧急通知
     */
    public static void sendUrgentNotification(Long userId, Object notification) {
        WebSocketSession session = userSessions.get(userId);
        if (session != null && session.isOpen()) {
            try {
                ObjectMapper mapper = new ObjectMapper();
                Map<String, Object> message = Map.of(
                    "type", "urgent_notification",
                    "data", notification,
                    "timestamp", System.currentTimeMillis(),
                    "priority", "urgent"
                );
                String jsonMessage = mapper.writeValueAsString(message);
                session.sendMessage(new TextMessage(jsonMessage));
                log.info("发送紧急通知给用户 {}: {}", userId, notification);
            } catch (Exception e) {
                log.error("发送紧急WebSocket通知给用户 {} 失败", userId, e);
            }
        }
    }

    /**
     * 从session中获取用户ID
     */
    private Long getUserIdFromSession(WebSocketSession session) {
        String query = session.getUri().getQuery();
        if (query != null && query.contains("userId=")) {
            String userIdStr = query.substring(query.indexOf("userId=") + 7);
            if (userIdStr.contains("&")) {
                userIdStr = userIdStr.substring(0, userIdStr.indexOf("&"));
            }
            try {
                return Long.valueOf(userIdStr);
            } catch (NumberFormatException e) {
                log.warn("无法解析用户ID: {}", userIdStr);
            }
        }
        return 1L; // 默认用户ID
    }

    /**
     * 移除会话
     */
    private void removeSession(WebSocketSession session) {
        Long userId = getUserIdFromSession(session);
        if (userId != null) {
            userSessions.remove(userId);
            log.info("移除用户 {} 的通知WebSocket连接", userId);
        }
    }

    /**
     * 获取在线用户数量
     */
    public static int getOnlineUserCount() {
        return userSessions.size();
    }

    /**
     * 检查用户是否在线
     */
    public static boolean isUserOnline(Long userId) {
        WebSocketSession session = userSessions.get(userId);
        return session != null && session.isOpen();
    }
} 